﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using ConnectionBase;
using CoreEveprod;

namespace EveProd
{
	public class Calculer
	{
		#region Attributs
		private ProfilDll _profilProducteur;
		private bool _tech1;
		private bool _tech2;
		private bool _tech3;
		private delegate void MyDelegateProgressBar(int nbre);
		private delegate void MyDelegateActivationForm();

		// <idRegion, idSystem, idStation>
		private Tuple<int, int, int> _pointDeVente;
		private Tuple<int, int, int> _pointAchat;

		// Ma connexion à la base de donnée
		private DataClassesEveProdDataContext _dataContextBase = new DataClassesEveProdDataContext();
		#endregion

		#region Get & Set
		public ProfilDll ProfilProducteur
		{
			get { return _profilProducteur; }
			set { _profilProducteur = value; }
		}

		public bool Tech1
		{
			get { return _tech1; }
			set { _tech1 = value; }
		}

		public bool Tech2
		{
			get { return _tech2; }
			set { _tech2 = value; }
		}

		public bool Tech3
		{
			get { return _tech3; }
			set { _tech3 = value; }
		}

		public DataClassesEveProdDataContext DataContextBase
		{
			get { return _dataContextBase; }
			set { _dataContextBase = value; }
		}

		public Tuple<int, int, int> PointDeVente
		{
			get { return _pointDeVente; }
			set { _pointDeVente = value; }
		}

		public Tuple<int, int, int> PointAchat
		{
			get { return _pointAchat; }
			set { _pointAchat = value; }
		}

		#endregion

		#region Constructeur
		/// <summary>
		/// Constructeur pour lancer un calcul sur les Technologies.
		/// </summary>
		/// <param name="checkTech1"></param>
		/// <param name="checkTech2"></param>
		/// <param name="checkTech3"></param>
		/// <param name="achat"></param>
		/// <param name="vente"></param>
		public Calculer(bool checkTech1, bool checkTech2, bool checkTech3, string achat, string vente)
		{
			// Je prend le profil.
			ProfilProducteur = ProfilProvider.GetProfil();
			Tech1 = checkTech1;
			Tech2 = checkTech2;
			Tech3 = checkTech3;

			// Détermination des ID -région,system,station- de vente et Achat.
			PointAchat = RecupererIds(achat);
			PointDeVente = RecupererIds(vente);
		}

		/// <summary>
		/// Constructeur pour lancer un calcul pour un Blueprint unitaire.
		/// </summary>
		/// <param name="checkUnitaire"></param>
		/// <param name="achat"></param>
		/// <param name="vente"></param>
		public Calculer(string achat, string vente)
		{
			// Je prend le profil.
			ProfilProducteur = ProfilProvider.GetProfil();

			// Détermination des ID -région,system,station- de vente et Achat.
			PointAchat = RecupererIds(achat);
			PointDeVente = RecupererIds(vente);
		}
		#endregion

		/// <summary>
		/// Méthode qui permet de chercher les ids -Région, System,Station -
		/// </summary>
		/// <param name="nomStation"></param>
		/// <returns></returns>
		private Tuple<int, int, int> RecupererIds(string nomStation)
		{
			// Je récupère les IDs de la station donnée
			var station = (from ii in DataContextBase.Stations
						   where ii.NomStation == nomStation
						   select ii).First();

			Tuple<int, int, int> retourIds = new Tuple<int, int, int>(station.IdRegion, station.IdSystem, station.IdStation);
			return retourIds;
		}

		/// <summary>
		/// En fonction des attributs de la classe, je fais du LinQ pour récupérer la liste de BP de Tech sélectionné.
		/// </summary>
		public void CalculerBenefice()
		{
			// Je supprime toutes les données avants, pour ne pas avoir de conflit de PK.
			_dataContextBase.Delete_ResultatCalcul();

			// Comme le Tech1 n'utilise que 6 types de ressources (les minerais), je vais commencer par eux.
			if (Tech1)
			{
				var listBlueprintTech1 = (from bp in DataContextBase.Blueprint
										  where bp.TechLvl == 1
										  select bp);

				CalculBluePrint(listBlueprintTech1);
			}

			if(Tech2)
			{
				var listBlueprintTech2 = (from bp in DataContextBase.Blueprint
										  where bp.TechLvl == 2
										  select bp);
				CalculBluePrint(listBlueprintTech2);
			}

			MessageBox.Show("Calcul Terminé", "Terminé", MessageBoxButtons.OK, MessageBoxIcon.Information);

			var main = DesignPatternSuperControleur.MainForm;
			main.BeginInvoke(new MyDelegateActivationForm(main.MettreFormEnFonction));
		}

		/// <summary>
		/// Cherche le Blueprint dans la table Blueprint, en fonction du nom du Blueprint passé en paramètre.
		/// </summary>
		/// <param name="nomBlueprint">Nom du blueprint à trouver.</param>
		public void CalculerBeneficeUnitaire(string nomBlueprint)
		{
			// Je supprime toutes les données avants, pour ne pas avoir de conflit de PK.
			_dataContextBase.Delete_ResultatCalcul();

			var leBlueprint = (from bp in DataContextBase.Blueprint
							   where bp.ItemsEve.NomItem == nomBlueprint
							   select bp);
			CalculBluePrint(leBlueprint);
		}

		/// <summary>
		/// Méthode qui va faire le calcul pour le Tech1
		/// </summary>
		/// <param name="listBlueprint"></param>
		private void CalculBluePrint(IQueryable<Blueprint> listBlueprint)
		{
			MeilleurPrix moinsChere = new MeilleurPrix(PointAchat, PointDeVente);
			IndustryCalcul industryCalcul = new IndustryCalcul(ProfilProvider.GetProfil().SkillProdEfficiency);
			IndustryCalcul indusTime = new IndustryCalcul(ProfilProvider.GetProfil().SkillIndustry);
			
			// Cette partie c'est pour l'affichage dans la progressBar de calcul.
			var nbreBlueprint = listBlueprint.Count();
			var main = DesignPatternSuperControleur.MainForm;
			main.BeginInvoke(new MyDelegateProgressBar(main.PreparationProgressBar), nbreBlueprint);

			var prod = (from pp in DataContextBase.Products
						join bp in listBlueprint on pp.IdProduct equals bp.IdProduct
						group pp by pp.IdProduct);

			// Maintenant pour chaque produit, je récupère les matériaux et leurs quantitées, ainsi que leur prix (achat - vente)
			foreach (var compos in prod)
			{
				decimal prixTotal = 0;
				bool boolBreak = false;
				List<ResultatCalculDetail> listDetail = new List<ResultatCalculDetail>();
				ResultatCalculDetail calculDetail = new ResultatCalculDetail();

				// Je récupère le ME et le PE du Blueprint.
				var indus = (from bp in listBlueprint
						  where compos.Key == bp.IdProduct
						  select new
						         	{
						         		bp.MaterielEfficiency, 
										bp.ProductionEfficiency,
										bp.ProductTime,
										bp.ProductionModifier
						         	}).First();

				#region Temps Production

				// Nous devons recalculer le temps de production.
				int tempsProd = indusTime.NouveauTempsProd(indus.ProductionEfficiency, indus.ProductTime, indus.ProductionModifier);

				#endregion

				#region Achat
				// Boucle pour calculer le cout de production.
				foreach (var pp in compos)
				{
					decimal prix;
					// Je dois appliquer en fonction des skills du joueurs et du ME une modification des quantités.
					int newQuantite = industryCalcul.NouvelleQuantite(pp.quantite, indus.MaterielEfficiency, pp.wasted);

					// Je check si j'ai le prix en "mémoire", sinon je vais le chercher en base.
					if (MarketPriceProvider.CheckPrice(pp.IdMateriel))
					{
						prix = MarketPriceProvider.DonneLePrix(pp.IdMateriel);
					}
					else
					{
						prix = moinsChere.PasCherePasChere(pp.IdMateriel);
						MarketPriceProvider.AjoutPrix(pp.IdMateriel, prix);
					}

					// Si le prix est supérieur à zéro.
					if (prix > 0)
					{
						decimal prixFinal = newQuantite * prix;
						prixTotal += prixFinal;

						calculDetail = new ResultatCalculDetail
						               	{
						               		IdItemEve = compos.Key,
						               		MaterielRequisYou = pp.IdMateriel,
						               		QuantiteYou = newQuantite,
						               		PrixUnitaire = prix,
						               		PrixTotal = prix*newQuantite,
						               		TempsProdYou = tempsProd
						               	};
						listDetail.Add(calculDetail);
					}
					else
					{
						// Si une valeur est à zéro, je ne dois pas continuer le calcul, je sors.
						boolBreak = true;
						break;
					}
				}
				#endregion

				#region Vente
				// Je vais chercher le prix de vente de ce produit.
				decimal prixVente = moinsChere.PrixDeVente(compos.Key);

				#endregion

				// Si le boolBreak est False, alors je sauvegarde.)
				if (!boolBreak && prixVente > 0)
				{
					ResultatCalcul resultatCalcul = new ResultatCalcul();
					resultatCalcul.IdItemEve = compos.Key;
					resultatCalcul.IdStationAchat = _pointAchat.Item3;
					resultatCalcul.IdStationVente = _pointDeVente.Item3;
					resultatCalcul.CoutProduction = prixTotal;
					resultatCalcul.PrixVente = prixVente;
					resultatCalcul.Benefice = prixVente - prixTotal;
					resultatCalcul.DateCalcul = DateTime.Now;
					resultatCalcul.TempsIsk = resultatCalcul.Benefice/tempsProd;

					DataContextBase.ResultatCalcul.InsertOnSubmit(resultatCalcul);
					DataContextBase.ResultatCalculDetail.InsertAllOnSubmit(listDetail);
					
					DataContextBase.SubmitChanges();
				}

				main.BeginInvoke(new MyDelegateProgressBar(main.AvancerProgressBar), 0);
			}
		}

	}
}
